
namespace hms
{


template<class T>
EnableIfFundamental<T,T> read( std::istream& is )
{
	T t;
	if ( !(is >> t) )
	{
		is.clear();
		throw IOStreamError(
			std::string{ "Could not read value from stream! "
				"Expected type \"" } + typeid(T).name() + "\", got \""
				+ unexpectedToken(is) + "\"."
		);
	}
	return t;
}

template<typename T>
EnableIfFundamental<T, std::istream&> read(std::istream& is, std::vector<T>& vec
){
	for ( T& entry : vec )
		entry = read<T>(is);
	
	return is;
}

template<typename T>
typename std::enable_if_t<std::is_same_v<T,std::string>, std::string> read(
	std::istream& is
){
	std::string str;
	is >> str;
	return str;
}


template<typename T>
EnableIfFundamental<T, std::ostream&> write( std::ostream& os, T t){
	if constexpr( std::is_floating_point<T>::value )
	{
		os << std::setprecision(hms::io::writePrecision) << t;
		return os;
	}
	return os << t;
}

template<typename T>
EnableIfFundamental<T, std::ostream&> write(
	std::ostream& os, const std::vector<T>& vec
){
	for ( decltype(vec.size()) i {0}; i<vec.size(); ++i ){
		write(os, vec[i]);
		if (i < vec.size()-1) os << " ";
	}
	return os;
}


template<typename T>
EnableIfFundamental<T, std::string> toString( T t )
{
	/* for floating points, use stringstream for conversion, so that
	 * precision can be set */
	if constexpr( std::is_floating_point<T>::value )
	{
		std::stringstream ss;
		write(ss, t);
		return ss.str();
	}
	/* for all other types, std::to_string is sufficient */
	else return std::to_string(t);
}


template <typename T>
EnableIfFundamental<T, std::string> toString( const std::vector<T>& v )
{
	std::stringstream ss;
	write(ss, v);
	return ss.str();
}

} // end namespace hms
